/**    
 * 文件名：CRCUtil.java<br/>
 * 版本信息：<br/>    
 * 日期：2014-8-19<br/>    
 * Copyright www.szholly.com 2014 版权所有<br/>     
 */
package com.btcode.common;

import org.apache.commons.lang3.StringUtils;

import java.io.UnsupportedEncodingException;

/**        
 * 项目名称：SludgeMonitor<br/>    
 * 类名称：CRCUtil<br/>    
 * 类描述：CRC校验工具类<br/>
 * 修改时间：2014-8-19 下午6:58:30<br/>    
 * 修改备注：
 * @since 2014-8-19 下午6:58:30 
 * @version 1.0         
 */
@SuppressWarnings("ALL")
public class CRCUtil {

	/**
	 * 计算部分，不包含报头##和数据长度
	 * 生成CRC校验码<br>
	 * 算法说明<br>
     * 1:装入一值全是“1”的16位寄存器。<br>
     * 2.取被检验串的一个字节与16位寄存器的高位字节进行异或运算，
     * 运算结果放入这个16位寄存器。<br>
     * 3.把16位寄存器向右移一位。<br>
     * 4.若向右移出的数位是1，则生成多项式1010 0000 0000 0001和这个
     * 寄存器进行异或运算，若向右移出的数位是0则返回(3)。<br>
     * 5.重复(3)和(4)，直至移出8位。<br>
     * 6.取被检验的下一个字节。<br>
     * 7.重复(2)-(6)直至被检验的所有字节均与16位寄存器进行异或运算，
     * 并移位8次。<br>
     * 8.这个寄存器的内容即为两个字节的检验码。
     * 检验码按先高字节后低字节的顺序存放。
	 **/

    public static String CalcCRC(String strData) {
    	
    	if(strData.startsWith("##")){
    		strData = strData.substring(6);
    	}
    	    	
        //多项式1010 0000 0000 0001的十六进制
        int multiterm = 0xA001;
        //字节数组
        byte[] dataArray = null;
        //1.装入一值全是“1”的16位寄存器
        int uCRC = 0xFFFF;
        //判断字符串数据不为空
        //noinspection ConstantConditions
        if (strData != null && !"".equals(strData)) {
            //字符串数据转为字节数组
            try {
				dataArray = strData.getBytes("ASCII");
			} catch (UnsupportedEncodingException e) {
				return null;
			}
            //7.重复(2)-(6)直至被检验的所有字节均与16位寄存器进行异或运算
            for (byte aDataArray : dataArray) {
                //2.取被检验串的一个字节与16位寄存器的高位字节进行异或运算,运算结果放入这个16位寄存器
                uCRC = (int) (aDataArray ^ (uCRC >> 8));
                //5.重复(3)和(4)，直至移出8位
                for (int j = 0; j < 8; j++) {
                    //4.若向右移出的数位是1，则生成多项式1010 0000 0000 0001和这个寄存器进行异或运算，若向右移出的数位是0则返回3.
                    if ((uCRC & 0x0001) == 1) {
                        //3.把16位寄存器向右移一位
                        uCRC >>= 1;
                        uCRC ^= multiterm;
                        continue;
                    }
                    //3.把16位寄存器向右移一位
                    uCRC >>= 1;
                }
            }
        }
        
        return StringUtils.leftPad(Integer.toHexString(uCRC).toUpperCase(), 4, "0") ;
    }
    
    /**
     * 计算一个完整报文的CRC码（已经包含CRC的报文）
     * @param msg
     * @return
     */

    private static String CalcCRCHoldData(String msg) {
    	int msgLength = msg.length();
		String realMsg = "";
		if(msg.endsWith("&&")){
			realMsg = msg;
		}
		else{
			realMsg = msg.substring(6, msgLength - 4);
		}
        return CRCUtil.CalcCRC(realMsg);
    }
    
    /**
     * 验证报文的CRC是否正确
     * @param msg
     * @return
     */

    public static boolean match(String msg){
    	msg = msg.trim();
    	int msgLength = msg.length();
		String CRC = msg.substring(msgLength - 4, msgLength);
		CRC = CRC.toUpperCase();
		String r1 = CRCUtil.CalcCRCHoldData(msg).toUpperCase();
        return CRC.equals(r1);
    }

    public void testCRC(){
        String msg = "##0398QN=20190509010001000;ST=32;CN=2061;PW=123456;MN=399HOLL0000389;Flag=1;CP=&&DataTime=20190509010001;B01-Min=null,B01-Max=null,B01-Avg=0.000491,B01-Cou=0.000491,B01-Flag=N;028-Min=null,028-Max=null,028-Avg=0.046452,028-Cou=0.0,028-Flag=N;cond-Min=null,cond-Max=null,cond-Avg=1.978,cond-Cou=null,cond-Flag=N;B01TOTAL-Min=null,B01TOTAL-Max=null,B01TOTAL-Avg=0.0,B01TOTAL-Cou=0.0,B01TOTAL-Flag=N&&";
        msg = msg + CRCUtil.CalcCRC(msg);
        System.out.println(msg);
    }
}
